using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;
using Microsoft.Xna.Framework.Content;
using JigLibX.Physics;
using JigLibX.Collision;
using DARE.Properties;
using System.Xml;
using System.Reflection;

namespace DARE
{
    public sealed class CDare : DrawableGameComponent
    {

    #region fields

        private Random m_random;
        private static CDare instance;
        private Game m_game;
        private SpriteBatch m_spriteBatch;
        private CNetworkMgr m_networkMgr;
        private CEffectMgr m_effectMgr;
#if !WINDOWS_PHONE
        private CAudioMgr m_audioMgr;
#endif
        private CInputMgr m_inputMgr;
        private CSceneMgr m_sceneMgr;
        private CDebugMgr m_debugMgr;
        //private CEmitterMgr m_emitterMgr;
        private List<IUpdateable> m_updatableComponents;
        private List<IDrawable> m_drawableComponents;
        private bool m_autoClearInput;
        private bool m_hasWindowChanged;
        private BasicEffect m_xnaBasicEffect;
        private CBasicEffect m_basicEffect;
        private CConfig m_config;
        private GraphicsDeviceManager m_graphicsDeviceManager;
        private int m_idCounter;

    #endregion

    #region properties

        public GraphicsDevice GraphicsDevice { get { return m_graphicsDeviceManager.GraphicsDevice; } }

        public bool HasWindowChanged 
        { 
            get { return m_hasWindowChanged; } 
            set { m_hasWindowChanged = value; } 
        }

        public CDebugMgr DebugMgr
        {
            get { return m_debugMgr; }
        }

        public static CDare Instance
        {
            get { return instance; }
        }

        public SpriteBatch SpriteBatch 
        { 
            get { return m_spriteBatch; } 
        }

        public CNetworkMgr NetworkMgr 
        { 
            get { return m_networkMgr; } 
        }

        public CEffectMgr EffectMgr 
        { 
            get { return m_effectMgr; } 
        }
#if !WINDOWS_PHONE
        public CAudioMgr AudioMgr 
        { 
            get { return m_audioMgr; } 
        }
#endif
        public CInputMgr InputMgr
        { 
            get { return m_inputMgr; } 
        }

        public CSceneMgr SceneMgr 
        { 
            get { return m_sceneMgr; } 
        }

        public List<IUpdateable> UpdatableComponent 
        { 
            get { return m_updatableComponents; }
        }

        public List<IDrawable> DrawableComponent 
        { 
            get { return m_drawableComponents; } 
        }

        public bool AutoClearInputs 
        { 
            get { return m_autoClearInput; } 
            set { m_autoClearInput = value; } 
        }

        public Random Random
        {
            get { return m_random; }
        }

        public CBasicEffect BasicEffect
        {
            get { return m_basicEffect; }
        }

        public BasicEffect XnaBasicEffect
        {
            get { return m_xnaBasicEffect; }
        }

        public CConfig Config
        {
            get { return m_config; }
        }

        public GraphicsDeviceManager GraphicsDeviceManager
        {
            get { return m_graphicsDeviceManager; }
        }

        public int IdCounter
        {
            get { return ++m_idCounter; }
            set { m_idCounter = value; }
        }

        public string IdCounterString
        {
            get { return IdCounter.ToString(); }
        }

        public int LastId
        {
            get { return m_idCounter; }
        }

        public string LastIdString
        {
            get { return m_idCounter.ToString(); }
        }

        //public CEmitterMgr EmitterMgr
       // {
       //     get { return m_emitterMgr; }
       // }

    #endregion

    #region ctor

        public CDare(Game game)
            : base(game)
        {
            Initialize(game);
        }

        private void Initialize(Game game)
        {
            Debug.Assert(instance == null, "This class can be created only once!");
            Debug.Assert(game != null, "The Game public class in the Sconfig structure is null. You must specify the Game public class used for this Engine.");
            instance = this;
            m_game = game;
            m_idCounter = -1;
            m_game.Components.Add(this);

            m_drawableComponents = new List<IDrawable>();
            m_updatableComponents = new List<IUpdateable>();
            if ((m_graphicsDeviceManager = m_game.Services.GetService(typeof(IGraphicsDeviceManager)) as GraphicsDeviceManager) == null)
                m_graphicsDeviceManager = new Microsoft.Xna.Framework.GraphicsDeviceManager(m_game);

            m_networkMgr = new CNetworkMgr();
            m_random = new Random();
#if !WINDOWS_PHONE
            m_audioMgr = new CAudioMgr();
#endif
            m_sceneMgr = new CSceneMgr();
            m_config = new CConfig(this);
            m_hasWindowChanged = false;
            m_autoClearInput = true;
        }
        
    #endregion

    #region RockNRoll

        /// <summary>
        /// Initialize the core with the specified configuration.
        /// </summary>
        public void RockNRoll()
        {

            m_spriteBatch = new SpriteBatch(m_game.GraphicsDevice);
            m_xnaBasicEffect = new BasicEffect(m_game.GraphicsDevice);
            //m_emitterMgr = new CEmitterMgr();
#if !WINDOWS_PHONE
            m_basicEffect = new CBasicEffect();
#endif
            m_debugMgr = new CDebugMgr();
            m_inputMgr = new CInputMgr();
            IntializeDefaultResources();
            m_graphicsDeviceManager.DeviceReset += DeviceReset;
            DeviceReset(null, EventArgs.Empty);

        }

        public void IntializeDefaultResources()
        {
            PreloadResources();
            //CreateAudio();
        }

        private void PreloadResources()
        {
#if WINDOWS
            XmlDocument xdoc = new XmlDocument();
            xdoc.LoadXml(Resources.ResourcesConf);
            XmlNodeList nodes = xdoc.GetElementsByTagName("res");
            foreach (XmlNode node in nodes)
            {
                string type = node.Attributes["type"].Value;
                if (type.IndexOf("Microsoft.Xna.Framework.Graphics.") == -1)
                    type = "Microsoft.Xna.Framework.Graphics." + type;
                Type t = System.Reflection.Assembly.GetAssembly(typeof(Texture2D)).GetType(type);
                byte[] o = (byte[])Resources.ResourceManager.GetObject(node.Attributes["resource-name"].Value);
                string name = node.Attributes["name"].Value;

                MethodInfo readAG = typeof(CResourceMgr).GetMethod("Load", new Type[2] { typeof(byte[]), typeof(string) }).MakeGenericMethod(t);
                object[] obj = new object[2] { o, name };
                if (o != null)
                    readAG.Invoke(null, obj);
            }
#endif
        }

    #endregion

    #region methods

        public void ResetIdCounter()
        {
            m_idCounter = -1;
        }

        public void DeviceReset<EventArgs>(object sender, EventArgs e)
        {
            m_hasWindowChanged = true;
            m_debugMgr.DebugConsole.Rectangle = new Rectangle(0, CDare.Instance.GraphicsDevice.Viewport.Height - (int)(CDare.Instance.GraphicsDevice.Viewport.Height * .45f), (int)(CDare.Instance.GraphicsDevice.Viewport.Width * .45f), (int)(CDare.Instance.GraphicsDevice.Viewport.Height * .45f));
            Config.Video.WindowRatio = new Vector2(((float)Config.Video.ResolutionWidth / (float)Game.Window.ClientBounds.Width), ((float)Config.Video.ResolutionHeight / (float)Game.Window.ClientBounds.Height));
            m_debugMgr.DebugConsole.WriteLine(Config.Video.WindowRatio.ToString());
        }

        public override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {

        }

        protected override void UnloadContent()
        {
        }

        // OBSOLETE, CONFIGURE IN SOUNDMGR.STARTENGINE(path, path, path)
        ///// <summary>
        ///// Create the audio system for spatial sound.
        ///// Creation work one time, next call to this function will be ignored.
        ///// <remarks>
        ///// If you set the paths for audio in the xml and in the code, the code will be chosen because it's created first.
        ///// </remarks>
        ///// </summary>
        //public void CreateAudio()
        //{
        //    if (m_config.Audio.PathFilled)
        //    {
        //        Console.WriteLine("Creating audio...");
        //        m_audioMgr.SoundMgr.CreateAudioEngine(m_config.Audio.AudioEnginePath);
        //        m_audioMgr.SoundMgr.CreateSoundBank(m_config.Audio.SoundBankPath);
        //        m_audioMgr.SoundMgr.CreateWaveBank(m_config.Audio.WaveBankPath);
        //    }
        //}

        public override void Update(GameTime gameTime)
        {
            m_config.Time.Update(gameTime);

            if (m_autoClearInput)
                m_inputMgr.Clear();
            m_inputMgr.Update();
            if (m_sceneMgr != null)
                m_sceneMgr.Update(gameTime);
            for (int i = 0; i < m_updatableComponents.Count; ++i)
                if (m_updatableComponents[i].Updatable)
                    m_updatableComponents[i].Update(gameTime);
            m_hasWindowChanged = false;
        }

        public override void Draw(GameTime gameTime)
        {
            lock (m_game.GraphicsDevice)
            {
                m_game.GraphicsDevice.Clear(m_config.Video.ClearColor);
                if (m_sceneMgr != null)
                    m_sceneMgr.Draw(gameTime);
                foreach (IDrawable drawable in m_drawableComponents)
                    if (drawable.Drawable)
                        drawable.Draw(gameTime);
                m_debugMgr.Draw(gameTime);
            }
        }

    #endregion

    }
}
